package cn.cqautotest.downloadfile.download

import android.os.Handler
import android.os.Looper
import android.util.Log
import cn.cqautotest.downloadfile.network.ServiceCreator
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
import java.io.FileOutputStream
import java.lang.RuntimeException

/**
 * 下载文件的请求实现类（单例类）
 */
object DownloadFileRequest {

    private const val TAG = "DownloadFileRequest"
    // 实例化一个运行在主线程中的 Handler ，这个 Handler 接收的 Looper 对象是主线程的 Looper，所以可以用它来更新UI
    private val handler = Handler(Looper.getMainLooper())

    /**
     * 真正进行下载文件的方法
     *
     * @param downloadUrl String 需要下载文件的url
     * @param localFilePath String 本地文件的目录
     * @param localFileName String 本地文件的名称
     * @param downloadListener DownloadFileListener 文件下载的监听
     */
    fun download(
        downloadUrl: String,
        localFilePath: String,
        localFileName: String,
        downloadListener: DownloadFileListener
    ) {
        val file = File(localFilePath, localFileName)
        val keepDownload = downloadListener.onBeforeBegin(file, file.exists())
        // Kotlin 中的 not 函数对运算符进行了重载，运算符重载就是对已有的运算符赋予他们新的含义，重载的修饰符是 operator
        if (keepDownload.not()) return
        val downloadApi = ServiceCreator.create<DownloadFileApi>()
        val execute = downloadApi.download(downloadUrl)
        execute?.enqueue(object : Callback<ResponseBody?> {
            override fun onResponse(call: Call<ResponseBody?>, response: Response<ResponseBody?>) {
                val runCatching = response.body()?.runCatching {
                    // 需要下载文件的总大小
                    val totalLength = contentLength()
                    // 当前已下载的文件长度
                    var currentLength: Long = 0
                    val inputStream = byteStream()
                    val fileOutputStream = FileOutputStream(file)
                    // 一般都是 1024 ，我把这个值设置大一点是为了下载的速度更快，暂时没有发现什么异常情况
                    val bytes = ByteArray(totalLength.toInt())
                    var len = 0
                    while (len != -1) {
                        fileOutputStream.write(bytes, 0, len)
                        fileOutputStream.flush()
                        currentLength += len
                        onProgressChanged(downloadListener, currentLength, totalLength, downloadUrl)
                        len = inputStream.read(bytes)
                    }
                    fileOutputStream.flush()
                    fileOutputStream.close()
                    inputStream.close()
                    // 通知文件已经下载完成
                    onFinished(downloadListener, currentLength, downloadUrl, file)
                    return
                }
                runCatching?.isFailure.run {
                    Log.d(TAG, "onResponse: ====> 去看看打印的堆栈信息吧！")
                    runCatching?.exceptionOrNull()?.printStackTrace()
                    onFailure(downloadListener, file, runCatching?.exceptionOrNull())
                }
            }

            override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
                Log.d(TAG, "onResponse: ====> 去看看打印的堆栈信息吧！")
                t.printStackTrace()
                onFailure(downloadListener, file, t)
            }
        })
        if (execute == null) {
            onFailure(
                downloadListener, file, RuntimeException("ExecutorCallbackCall is null！")
            )
        }
    }

    fun onProgressChanged(
        downloadListener: DownloadFileListener,
        currentLength: Long,
        totalLength: Long,
        downloadUrl: String
    ) {
        handler.post {
            downloadListener.onProgressChanged(currentLength, totalLength, downloadUrl)
        }
    }

    fun onFinished(
        downloadListener: DownloadFileListener,
        currentLength: Long,
        downloadUrl: String,
        file: File
    ) {
        handler.post {
            downloadListener.onFinished(currentLength, downloadUrl, file)
        }
    }

    fun onFailure(downloadListener: DownloadFileListener, file: File, t: Throwable?) {
        handler.post {
            downloadListener.onFailure(file, t)
        }
    }
}